package com.coscon.shiro;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/** 

 * hasPermission支持复杂表达式（使用逆波兰表达式计算）
 * @author 409390047@qq.com
 */  
public abstract class AbstractOperatorAuthorizingRealmWithRpn extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(AbstractOperatorAuthorizingRealmWithRpn.class);
  
    //支持的运算符和运算符优先级  
    public static final Map<String, Integer> EXP_MAP = new HashMap<String, Integer>(){{
        put("not",0);  
        put("!"  ,0);  
  
        put("and",0);  
        put("&&" ,0);  
  
        put("or" ,0);  
        put("||" ,0);  
  
        put("("  ,1);  
        put(")"  ,1);  
    }};  
  
    public static final Set<String> EXP_LIST = EXP_MAP.keySet();
  
    public AbstractOperatorAuthorizingRealmWithRpn() {
    }  
  
    public AbstractOperatorAuthorizingRealmWithRpn(CacheManager cacheManager) {
        super(cacheManager);  
    }  
  
    public AbstractOperatorAuthorizingRealmWithRpn(CredentialsMatcher matcher) {
        super(matcher);  
    }  
  
    public AbstractOperatorAuthorizingRealmWithRpn(CacheManager cacheManager, CredentialsMatcher matcher) {
        super(cacheManager, matcher);  
    }  
  
    @Override  
    public boolean isPermitted(PrincipalCollection principals, String permission) {  
        Stack<String> exp = getExp(EXP_LIST, permission);
        if (exp.size() == 1){  
            return super.isPermitted(principals, exp.pop());  
        }  
        List<String> expTemp = new ArrayList<>();  
        //将其中的权限字符串解析成true , false  
        for(String temp : exp){  
            if (EXP_LIST.contains(temp)){
                expTemp.add(temp);  
            }else{  
                expTemp.add(Boolean.toString(super.isPermitted(principals, temp)) );  
            }  
        }  
        logger.debug("permission:{}  Rpn:{}  parse:{}", permission, exp);  
        //计算逆波兰  
        return computeRpn(EXP_LIST, expTemp);
    }  
    
  
    private static boolean computeRpn(Collection<String> expList,Collection<String> exp){  
        logger.debug("RPN  exp :{}", exp);  
        Stack<Boolean> stack = new Stack<>();  
        for(String temp : exp){  
            if (expList.contains(temp)){  
                if ("!".equals(temp) || "not".equals(temp)){  
                    stack.push( !stack.pop() );  
                }else if ("and".equals(temp) || "&&".equals(temp)){  
                    Boolean s1 = stack.pop();  
                    Boolean s2 = stack.pop();  
                    stack.push(s1 && s2);  
                }else{  
                    Boolean s1 = stack.pop();  
                    Boolean s2 = stack.pop();  
                    stack.push(s1 || s2);  
                }  
            }else{  
                stack.push(Boolean.parseBoolean(temp));  
            }  
        }  
        if (stack.size() > 1){  
            logger.error("computeRpn RESULT ERROR>{}  exp:{}", stack, exp);  
            throw new RuntimeException("compute error！ stack: "+ exp.toString());  
        }else{  
            logger.debug("computeRpn RESULT SUCCESS>{}" , stack);  
            return stack.pop();  
        }  
    }  
  
    //获得逆波兰表达式  
    private static Stack<String> getExp(Collection<String> expList, String exp) {  
        Stack<String> s1 = new Stack<>();  
        Stack<String> s2 = new Stack<>();  
        for (String str : exp.split(" ")){  
            str = str.trim();  
            String strL = str.toLowerCase();  
            if ("".equals(str)){  
                continue;  
            }  
            if ("(".equals(str)){  
                //左括号  
                s1.push(str);  
            }else if (")".equals(str)){  
                //右括号  
                while(!s1.empty()){  
                    String temp = s1.pop();  
                    if ("(".equals(temp)){  
                        break;  
                    }else{  
                        s2.push(temp);  
                    }  
                }  
            }else if(expList.contains(strL)){  
                //操作符  
                if (s1.empty()){  
                    s1.push(strL);  
                }else {  
                    String temp = s1.peek();  
                    if ("(".equals(temp) || ")".equals(temp)){  
                        s1.push(strL);  
                    }else if(EXP_MAP.get(strL) >= EXP_MAP.get(temp)){
                        s1.push(strL);  
                    }else{  
                        s2.push(s1.pop());  
                        s1.push(strL);  
                    }  
                }  
            }else{  
                //运算数  
                s2.push(str);  
            }  
        }  
        while(!s1.empty()){  
            s2.push(s1.pop());  
        }  
        return s2;  
    }  
}  